package MONITORING::APPL::HP::NNM_CLUSTER;

#################################################################
# $LastChangedDate: 2013-11-25 15:03:06 +0000 (Mon, 25 Nov 2013) $
# $LastChangedRevision: 22 $
# $LastChangedBy: aswod_83@gmx.net $
#################################################################
use strict;
use base qw(moniCA);
use XML::Simple;
use Data::Dumper;
use Params::Validate qw(:all);

#-------------------------------------------------------------------------------
sub Run {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms: None
#-------------------------------------------------------------------------------
    my $self      = shift;
    my $cnf       = $self->GetCNF();
    my $logger    = $self->GetLogger();
    my $statefile = $self->TmpDir() . '/' . $self->Plug() . '.xml';
    my %newstat;

    #-------------------------------------
    #	validation of default parameters
    #-------------------------------------
    $self->_validCNFdefaults( $cnf->{defaults} );

    #-------------------------------------
    #	validation of identifier parameters
    #-------------------------------------
    $self->_validCNFidentifierAll();

    #-------------------------------------
    #	read cluster status from file
    #-------------------------------------

    $self->{ClusterStatus} = $self->_getClusterStatus(
        $self->{DefCfg}->{'SourceCmdMaxRetry'},
        sub {
            my @cs;
            open( LOGFILE, "<$self->{DefCfg}->{'SourceFile'}" );
            @cs = <LOGFILE>;
            close(LOGFILE);
            return @cs;
        }
    );

    #-------------------------------------
    #
    #-------------------------------------
    foreach ( keys %{ $self->{IdCfg} } ) {

        $self->{ID} = $_;
        Log::Log4perl::MDC->put( 'identifier', $self->{ID} );
        my %tmpstat;
        my $oldstat = XMLin( $statefile, ForceArray => 1 )->{identifier}->{$_}
          if ( -s $statefile );
        my $trigger = $self->{IdCfg}->{$_}->{'trigger'}
          || $self->{DefCfg}->{'trigger'};
        my $report = $self->{IdCfg}->{$_}->{'report'}
          || $self->{DefCfg}->{'report'};
        my ( $NodeType, $State, $OvStatus, $Hostname, $Address );
        foreach my $cm ( @{ $self->{ClusterStatus} } ) {

            next unless ( $cm =~ /$self->{IdCfg}->{$_}->{NodeTypeRef}/ );
            next unless ( $cm =~ /$self->{IdCfg}->{$_}->{HostnameRef}/ );

            $cm =~ s/^\*//;
            $cm =~ s/^\s+//;
            my @cms = split( /\s+/, $cm );
            $NodeType = @cms[1];
            $State    = @cms[2];
            $OvStatus = @cms[3];
            $Hostname = ( split( /\//, @cms[4] ) )[0];
            $Address  = ( split( /\//, @cms[4] ) )[1];

            if (   $State ne $self->{IdCfg}->{$_}->{'StateRef'}
                || $OvStatus ne $self->{IdCfg}->{$_}->{'OvStatusRef'} )
            {
                $self->{identifier}->{$_}->{bool} = 0;
                $tmpstat{failCnt} = ( $oldstat->{failCnt} + 1 ) || 1;
                if ( $tmpstat{failCnt} == $trigger ) {
                    $logger->error( "tEvent: "
                          . $self->{ID}
                          . " State: "
                          . $State
                          . " OvStatus: "
                          . $OvStatus . " ["
                          . $self->{IdCfg}->{$_}->{'StateRef'} . "|"
                          . $self->{IdCfg}->{$_}->{'OvStatusRef'}
                          . "]" );
                }
                else {
                    $logger->error( "iEvent: "
                          . $self->{ID}
                          . " State: "
                          . $State
                          . " OvStatus: "
                          . $OvStatus . " ["
                          . $self->{IdCfg}->{$_}->{'StateRef'} . "|"
                          . $self->{IdCfg}->{$_}->{'OvStatusRef'}
                          . "]" );
                }
            }
            else {
                $self->{identifier}->{$_}->{bool} = 1;
                if ( $oldstat->{failCnt} >= $trigger ) {
                    $logger->info( "cEvent: "
                          . $self->{ID}
                          . " State: "
                          . $State
                          . " OvStatus: "
                          . $OvStatus );
                }
                else {
                    $logger->info( "iEvent: "
                          . $self->{ID}
                          . " State: "
                          . $State
                          . " OvStatus: "
                          . $OvStatus );
                }
                $tmpstat{failCnt} = 0;
            }
        }

        $self->_validCNFrrd( $cnf->{rrd} ) && $self->_crReport()
          if ( $report =~ /on/i );
        $newstat{identifier}{$_} = \%tmpstat;
    }
    XMLout( \%newstat, outputfile => $statefile, NoSort => 0, XMLDecl => 1 );
}

#-------------------------------------------------------------------------------
sub _getClusterStatus {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my ( $self, $retrys, $func ) = @_;

  attempt: {
        my @cluster_status;
        eval {
            @cluster_status = $func->();
            1;
        };
        last attempt if $retrys < 1;
        unless (@cluster_status) {
            sleep 20;
            $retrys--;
            redo attempt;
        }
        return \@cluster_status;
    }

}

#-------------------------------------------------------------------------------
sub _crReport {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------

    my $self   = shift;
    my $logger = $self->GetLogger();

    eval {

        require RRDs;

        ( my $rrdident = $self->{ID} ) =~ s/\W/_/g;

        my $rrdfile =
            $self->{RRDCfg}->{rrdfilepath} . '/'
          . $self->Plug() . '_'
          . $rrdident . '.rrd'
          if ( -d $self->{RRDCfg}->{rrdfilepath} ) || die($!);
        my $rrdimg =
            $self->{RRDCfg}->{rrdimgpath} . '/'
          . $self->Plug() . '_'
          . $rrdident . '.png'
          if ( -d $self->{RRDCfg}->{rrdimgpath} ) || die($!);

        if ( !-f $rrdfile ) {

            my @rrdc;
            push( @rrdc, "$rrdfile" );
            push( @rrdc, "--step=$self->{RRDCfg}->{step}" );
            if ( ref( $self->{RRDCfg}->{DS} ) eq "ARRAY" ) {
                for ( @{ $self->{RRDCfg}->{DS} } ) { push( @rrdc, $_ ); }
            }
            else {
                push( @rrdc, $self->{RRDCfg}->{DS} );
            }
            for ( @{ $self->{RRDCfg}->{RRA} } ) { push( @rrdc, $_ ); }

            RRDs::create(@rrdc);

            $logger->warn( "RRDs::create returns " . RRDs::error() )
              if ( RRDs::error() );
        }

        my @identlist;
        if ( ref( $self->{RRDCfg}->{DS} ) eq "ARRAY" ) {
            for ( @{ $self->{RRDCfg}->{DS} } ) {
                my @tmplist = split( /:/, $_ );
                push( @identlist, $tmplist[1] );
            }
        }
        else {
            my @tmplist = split( /:/, $self->{RRDCfg}->{DS} );
            push( @identlist, $tmplist[1] );
        }
        my $dslist = join( ":", @identlist );

        if ( -f $rrdfile ) {
            my @rrdu;
            my @dsvalue;
            for (@identlist) {
                push( @dsvalue, $self->{identifier}->{ $self->{ID} }->{$_} );
            }
            my $dsvalue = join( ":", @dsvalue );

            push( @rrdu, "$rrdfile" );
            push( @rrdu, "--template=$dslist" );
            push( @rrdu, "N:$dsvalue" );

            RRDs::update(@rrdu);

            $logger->warn( "RRDs::update returns " . RRDs::error() )
              if ( RRDs::error() );

            my @rrdg;
            $rrdfile =~ s/\:\//\\\:\//g if ( $^O eq 'MSWin32' );

            push( @rrdg, "$rrdimg" );
            push( @rrdg, "--title=$self->{RRDCfg}->{'title'}: $self->{ID}" );
            push( @rrdg, "--start=$self->{RRDCfg}->{'start'}" );
            push( @rrdg, "--end=$self->{RRDCfg}->{'end'}" );
            push( @rrdg, "--vertical-label=$self->{RRDCfg}->{'label'}" );
            push( @rrdg, "--imgformat=$self->{RRDCfg}->{'imgformat'}" );
            push( @rrdg, "--watermark=$self->{RRDCfg}->{'watermark'}" );
            push( @rrdg, "--width=$self->{RRDCfg}->{'width'}" );
            push( @rrdg, "--height=$self->{RRDCfg}->{'height'}" );
            push( @rrdg, "--base=$self->{RRDCfg}->{'base'}" );
            push( @rrdg, "--upper-limit=$self->{RRDCfg}->{'upper-limit'}" );
            push( @rrdg, "--lower-limit=$self->{RRDCfg}->{'lower-limit'}" );

            foreach ( keys %{ $self->{RRDCfg}->{DEF} } ) {
                push( @rrdg,
                    "DEF:$_=$rrdfile:$_:$self->{RRDCfg}->{DEF}->{$_}->{CF}" );
                push( @rrdg,
"$self->{RRDCfg}->{DEF}->{$_}->{TYPE}:$_#$self->{RRDCfg}->{DEF}->{$_}->{COLOR}:$_ $self->{RRDCfg}->{DEF}->{$_}->{CF}"
                );
            }

            RRDs::graph(@rrdg);

            $logger->warn( "RRDs::graph returns " . RRDs::error() )
              if ( RRDs::error() );
        }
        1;
    } or $logger->debug( "Report incomplete: " . $@ );

}

#-------------------------------------------------------------------------------
sub _validCNFrrd {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;

    Params::Validate::validation_options( allow_extra => 1 );
    $self->{RRDCfg} = validate(
        @_,
        {
            'rrdfilepath' => {
                type     => SCALAR,
                default  => $self->VarDir(),
                optional => 1,
                regex    => qr/^.+$/
            },
            'rrdimgpath' => {
                type     => SCALAR,
                default  => $self->VarDir() . "/images",
                optional => 1,
                regex    => qr/^.+$/
            },
            'title' => { type => SCALAR },
            'label' => { type => SCALAR },
            'width' =>
              { type => SCALAR, default => "400", regex => qr/\d\d\d/ },
            'height' =>
              { type => SCALAR, default => "100", regex => qr/\d\d\d/ },
            'base'        => { type => SCALAR, default => 1000 },
            'upper-limit' => { type => SCALAR, default => 100 },
            'lower-limit' => { type => SCALAR, default => 0 },
            'start'       => { type => SCALAR, default => "end-24hours" },
            'end'         => { type => SCALAR, default => "now" },
            'imgformat'   => { type => SCALAR, regex   => qr/[PNG||GIF||PDF]/ },
            'watermark'   => {
                type    => SCALAR,
                default => "powered by andre.schmitt\@t-systems.com"
            },
            'DS'  => { type => ARRAYREF | SCALAR },
            'RRA' => { type => ARRAYREF | SCALAR }
        }
    );
    return $self;

}

#-------------------------------------------------------------------------------
sub _validCNFdefaults {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;

    Params::Validate::validation_options( allow_extra => 1 );
    $self->{DefCfg} = validate(
        @_,
        {
            Source  => { type => SCALAR, regex => qr/^(File||Cmd)$/ },
            report  => { type => SCALAR, regex => qr/^(on||off)$/i },
            trigger => { type => SCALAR, regex => qr/^.+$/ }
        }
    );
    return $self;
}

#-------------------------------------------------------------------------------
sub _validCNFidentifier {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;
    my $cnf  = $self->GetCNF();

    Params::Validate::validation_options( allow_extra => 0 );
    $self->{IdCfg}->{ $self->{ID} } = validate(
        @_,
        {
            HostnameRef => { type => SCALAR, regex => qr/^.+$/i },
            AddressRef  => { type => SCALAR, regex => qr/^.+$/i },
            OvStatusRef => { type => SCALAR, regex => qr/^.+$/i },
            StateRef    => { type => SCALAR, regex => qr/^.+$/i },
            NodeTypeRef => { type => SCALAR, regex => qr/^.+$/i },
            report =>
              { type => SCALAR, regex => qr/^(on||off)$/i, optional => 1 },
            trigger => { type => SCALAR, regex => qr/^.+$/, optional => 1 },

        }
    );
    return $self;
}

#-------------------------------------------------------------------------------
sub _validCNFidentifierAll {

#-------------------------------------------------------------------------------
# Description:
# Input Parms:
# Output Parms:
#-------------------------------------------------------------------------------
    my $self = shift;
    my $cnf  = $self->GetCNF();

    foreach ( keys %{ $cnf->{identifier} } ) {
        $self->{ID} = $_;
        $self->_validCNFidentifier( $cnf->{identifier}->{$_} );
    }
}
1;

__END__
 
=pod
 
=head1 NAME
 
SOCKET_PORT.pm - monitors service availability 
 
=head1 SYNOPSIS
 
=head1 DESCRIPTION
 
=head1 PREREQUISITES
 
The follwing CPAN modules needs to be installed:
 
C<Log::Log4perl>
C<XML::Simple>
C<Time::HiRes>

=head1 CONFIGURATION FILE
 
  By default
 
=head2 EXAMPLES
 
=head3 example of configuation file

=begin text

#################################################
Modul		MONITORING::APPL::NGSSM::NNM_CLUSTER
Logger		moniCA
LogLevel	INFO
Subject		moniCA HPNetworkNodeManagerCluster / ${APPL_NAME} / ${APPL_LAYER} / ${HOSTNAME}
Description	This moniCA plug checks ...

#DOC: 
#DOC: 
#DOC:	

<defaults>
	Source		=	File
	SourceFile	=	D:/Daten/nnmcluster-status.log
	SourceCmd	=	/usr/bin/sudo /opt/OV/bin/nnmcluster -display
	SourceFileMaxAge	=	800
	FailoverRef	=	Enabled
	report	=	off
	trigger	=	2
</defaults>

#DOC: 
#DOC: 
#DOC:	

<identifier ACTIVE_NNM>
	HostnameRef	=	nnm1
	AddressRef	=	192.168.2.10:7800
	OvStatusRef	=	RUNNING
	StateRef	=	ACTIVE_NNM_RUNNING
	NodeTypeRef	=	DAEMON
</identifier>

<identifier PASSIVE_NNM>
	HostnameRef	=	nnm2
	AddressRef	=	192.168.2.11:7800
	OvStatusRef	=	NOT_RUNNING
	StateRef	=	STANDBY_READY
	NodeTypeRef	=	DAEMON
</identifier>

=cut
